package com.wiscamp.ninechapters.problems.domain.services;


import com.wiscamp.ninechapters.problems.domain.ProblemsErrorCodes;
import com.wiscamp.ninechapters.problems.domain.models.exams.Exam;
import com.wiscamp.ninechapters.problems.domain.models.exams.ExamTypeRelation;
import com.wiscamp.ninechapters.problems.domain.models.exams.ScoringRule;
import com.wiscamp.ninechapters.problems.domain.repositories.ExamRepository;
import cube.common.cache.CacheManagerNames;
import cube.ddd.aspect.HandleServiceException;
import cube.ddd.domain.BaseDomainService;
import cube.ddd.exception.ExceptionUtils;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.Objects;

/**
 * Exam Service
 * 管理 Exam、ExamTypeRelation、ScoringRule 业务实体
 *
 * @author yifeng
 */
@Service
public class ExamService extends BaseDomainService {

    private final ExamRepository examRepository;

    private final String CACHE_NAME = "exams";

    public ExamService(ExamRepository examRepository) {
        this.examRepository = examRepository;
    }


    //region Exam

    @Cacheable(value = CACHE_NAME, key = "#examId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Exam getExam(@Min(0) long examId) {
        var exam = examRepository.getExam(examId);
        if (Objects.isNull(exam)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.EXAM_NOT_FOUND, examId);
        }
        return exam;
    }


    /*@HandleServiceException
    public Exam getExamByCode(@NotBlank String examCode, @Min(0) long organizationId) {
        return examRepository.getExamByCode(examCode, organizationId);
    }

    @HandleServiceException
    public Collection<Exam> getExamsByName(@NotBlank String examName, @Min(0) long organizationId) {
        return examRepository.getExamsByName(examName, organizationId);
    }

    @HandleServiceException
    public Collection<Exam> getExamsByOrganization(@Min(0) long organizationId) {
        return examRepository.getExamsByOrganization(organizationId);
    }

    @HandleServiceException
    public PagedObjects<Exam> getExamsByQuery(@NotNull PagedQuery query) {
        return examRepository.getExamsByQuery(query);
    }*/

    /**
     * Creates an exam
     *
     * @param exam
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#exam.examId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Exam createExam(@NotNull Exam exam, @Min(0) long operatorId) {
        var existedExam = examRepository.getExamByCode(exam.getExamCode(), exam.getOrganizationId());
        if (Objects.nonNull(existedExam)) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.EXAM_CODE_EXISTS, exam);
        }
        exam.createBy(operatorId);
        return examRepository.saveExam(exam);
    }

    /**
     * Updates an exam
     *
     * @param exam
     * @return
     */
    @CachePut(value = CACHE_NAME, key = "#exam.examId", cacheManager = CacheManagerNames.SIXTY_MINUTES_CACHE)
    @HandleServiceException
    public Exam updateExam(@NotNull Exam exam, long operatorId) {
        var existedExam = getExam(exam.getExamId());
        existedExam = examRepository.getExamByCode(exam.getExamCode(), exam.getOrganizationId());
        if (Objects.nonNull(existedExam) && existedExam.getExamId() != exam.getExamId()) {
            ExceptionUtils.throwServiceException(ProblemsErrorCodes.EXAM_CODE_EXISTS, exam);
        }
        exam.updateBy(operatorId);
        return examRepository.saveExam(exam);
    }

    /**
     * Deletes an exam
     *
     * @param examId
     * @return
     */
    @CacheEvict(value = CACHE_NAME, key = "#examId")
    @HandleServiceException
    public boolean deleteExam(@Min(0) long examId, long operatorId) {
        var exam = getExam(examId);
        if (Objects.isNull(exam)) return false;
        // 记录操作日志
        exam.delete();
        exam.updateBy(operatorId);
        examRepository.saveExam(exam);
        return true;
    }

    @CacheEvict(value = CACHE_NAME, key = "#examId")
    @HandleServiceException
    public boolean enableExam(@Min(0) long examId, long operatorId) {
        var exam = getExam(examId);
        if (Objects.isNull(exam)) return false;
        // 记录操作日志
        exam.enable();
        exam.updateBy(operatorId);
        examRepository.saveExam(exam);
        return true;
    }

    @CacheEvict(value = CACHE_NAME, key = "#examId")
    @HandleServiceException
    public boolean disableExam(@Min(0) long examId, long operatorId) {
        var exam = getExam(examId);
        if (Objects.isNull(exam)) return false;
        // 记录操作日志
        exam.disable();
        exam.updateBy(operatorId);
        examRepository.saveExam(exam);
        return true;
    }
    //endregion


    //region ExamTypeRelation

    /*@HandleServiceException
    public ExamTypeRelation getExamTypeRelation(@Min(0) long id) {
        return examRepository.getExamTypeRelation(id);
    }

    @HandleServiceException
    public Collection<ExamTypeRelation> getExamTypeRelationsByExam(@Min(0) long examId) {
        return examRepository.getTypeRelationsByExam(examId);
    }*/

    /**
     * Creates a relation of an exam and a problem type
     *
     * @param examTypeRelation
     * @return
     */
    @HandleServiceException
    public ExamTypeRelation createExamTypeRelation(@NotNull ExamTypeRelation examTypeRelation, long operatorId) {
        var existedExam = getExam(examTypeRelation.getExamId());
        return examRepository.saveExamTypeRelation(examTypeRelation);
    }

    @HandleServiceException
    public ExamTypeRelation updateExamTypeRelation(@NotNull ExamTypeRelation examTypeRelation, long operatorId) {
        var existedExam = getExam(examTypeRelation.getExamId());
        return examRepository.saveExamTypeRelation(examTypeRelation);
    }

    /**
     * Deletes a relation of an exam and a problem type
     *
     * @param id
     * @return
     */

    @HandleServiceException
    public boolean deleteExamTypeRelation(@Min(0) long id, long operatorId) {
        examRepository.deleteExamTypeRelation(id);
        return true;
    }

    //endregion


    //region scoringRule
    /*@HandleServiceException
    public ScoringRule getScoringRule(@Min(0) long id) {
        return examRepository.getScoringRule(id);
    }

    @HandleServiceException
    public ScoringRule getScoringRule(@Min(0) long examId, @Min(0) long problemTypeId, @Min(0) long questionTypeId) {
        return examRepository.getScoringRule(examId, problemTypeId, questionTypeId);
    }

    @HandleServiceException
    public Collection<ScoringRule> getScoringRulesByExam(@Min(0) long examId) {
        return examRepository.getScoringRulesByExam(examId);
    }*/

    @HandleServiceException
    public ScoringRule createScoringRule(@NotNull ScoringRule rule, long operatorId) {
        var existedExam = getExam(rule.getExamId());
        return examRepository.saveScoringRule(rule);
    }

    @HandleServiceException
    public ScoringRule updateScoringRule(@NotNull ScoringRule rule, long operatorId) {
        var existedExam = getExam(rule.getExamId());
        return examRepository.saveScoringRule(rule);
    }

    @HandleServiceException
    public boolean deleteScoringRule(@Min(0) long id, long operatorId) {
        examRepository.deleteScoringRule(id);
        return true;
    }
    //endregion
}
